package com.hk.core.autoconfigure.authentication;

import com.hk.core.authentication.api.SecurityContext;
import com.hk.core.authentication.api.UserPrincipal;
import com.hk.core.authentication.security.SpringSecurityContext;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.Objects;
import java.util.function.Function;

/**
 * @author Kevin
 * @date 2022-06-26 10:10
 */
@Configuration
@ConditionalOnClass(value = {PasswordEncoder.class})
public class CommonSecurityAutoConfiguration {

    /**
     * 密码编码器,支持多种方式的加密
     *
     * @return PasswordEncoder
     */
    @Bean
    @ConditionalOnMissingBean(value = {PasswordEncoder.class})
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    /**
     * 可以在Spring 中定义 Function 函数式Bean，泛型参数要和定义的一样
     *
     * <p>
     * 在 reactive环境中，不能使用jpa了，可以使用 r2dbc，所以，在reactive中，可不定义此bean
     * </p>
     * <a href="https://www.freesion.com/article/5415396843" />
     * <p>
     * 坑逼的操作,在 reactive环境,也需要配置此bean ? 因为 reactive还不支持jpa,对于jpa的
     * 审计{@link  org.springframework.data.domain.AuditorAware},获取不到登录的用户信息
     *
     * @return SpringSecurityContext
     * @see com.hk.core.authentication.security.reactive.filter.SecurityContextFilter
     * <p>
     */
    @Bean
    @ConditionalOnMissingBean(value = SecurityContext.class)
    public SpringSecurityContext securityContext(ObjectProvider<Function<UserPrincipal, ? extends UserPrincipal>> userInfoService) {
        var securityContext = new SpringSecurityContext();
        var function = userInfoService.getIfAvailable();
        if (Objects.nonNull(function)) {
            securityContext.setFunction(function);
        }
        return securityContext;
    }
}
